﻿using ComponentFactory.Krypton.Toolkit;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
using Nini.Config;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using TfsHelper.Model;
using TfsHelper.Model.DTOs;
using Outlook = Microsoft.Office.Interop.Outlook;

namespace TfsHelper.WinFrontEnd
{
    public partial class frmMain : KryptonForm
    {
        #region " Fields "

        private bool isDataBinding = false;
        private int minutes = 0;
        private int previousTrackingId = 0;
        private double round = 0;
        private long globalTicks = 0;

        private frmTracking tracking = new frmTracking();
        private frmDetails details = new frmDetails();

        private List<Meeting> colMeetings = new List<Meeting>();
        private List<DTOTime> colTimes;

        private IConfigSource config = new XmlConfigSource(Application.StartupPath + "\\Config.xml");

        #endregion

        #region " Constructor "

        public frmMain()
        {
            InitializeComponent();
            tracking.FormClosing += tracking_FormClosing;

            GetAppointmentsInRange();
        }

        #endregion

        #region " Properties "

        public IList<Model.Workitem> WICollection
        {
            get { return (IList<Model.Workitem>)gridWI.DataSource; }
        }

        #endregion

        #region " Handlers "

        private void frmMain_Load(object sender, EventArgs e)
        {
            timerProcess.Start();

            LoadInfo();

            tscmbView.SelectedIndex = 0;
            tscmbView_SelectedIndexChanged(null, EventArgs.Empty);
        }

        private void gridWI_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            if (isDataBinding)
                return;

            if (e.ColumnIndex == 6)
            {
                TimeSpan sum = new TimeSpan();
                gridWI.CommitEdit(DataGridViewDataErrorContexts.CurrentCellChange);
                foreach (DataGridViewRow row in gridWI.Rows)
                {
                    sum += TimeSpan.Parse(row.Cells["Time"].Value.ToString());
                }

                tslblSumToday.Text = "Today: " + sum.ToString("hh\\:mm") + " hrs.";
            }
        }

        private void timerProcess_Tick(object sender, EventArgs e)
        {
            var minutesNow = Math.Floor(DateTime.Now.TimeOfDay.TotalMinutes);

            #region " Stop Meetings "

            if (tracking.IsTracking && tracking.IsMeeting)
            {
                var meetingStop = (from Meeting meet in colMeetings
                                   where meet.Finish.Value.TimeOfDay.TotalMinutes == minutesNow && meet.MeetingId == tracking.MeetingId
                                   select meet).FirstOrDefault();

                if (meetingStop != null)
                {
                    notifyIcon.BalloonTipIcon = ToolTipIcon.Info;
                    notifyIcon.BalloonTipTitle = "Meeting Stop";
                    notifyIcon.BalloonTipText = "Meeting '" + meetingStop.Title + "' has finished. Autotrack is stoping.";
                    notifyIcon.ShowBalloonTip(4000);


                    if (previousTrackingId != 0)
                    {
                        tracking.StopTracking(false);
                        tracking.StartTracking(previousTrackingId, WICollection.First(x => x.WorkitemId == previousTrackingId).Title);
                        previousTrackingId = 0;
                    }
                    else
                    {
                        tracking.StopTracking(true);
                    }
                }
            }

            #endregion

            #region " Start Meetings "

            var meeting = (from Meeting meet in colMeetings
                           where meet.Start.Value.TimeOfDay.TotalMinutes == minutesNow
                           select meet).FirstOrDefault();

            if (meeting != null && tracking.MeetingId != meeting.MeetingId)
            {
                // If autostart is OFF
                if (!config.Configs["Meetings"].GetBoolean("Autostart"))
                {
                    notifyIcon.BalloonTipIcon = ToolTipIcon.Info;
                    notifyIcon.BalloonTipTitle = "Meeting Start Reminder";
                    notifyIcon.BalloonTipText = "Meeting " + meeting.Title + " is about to start.";
                    notifyIcon.ShowBalloonTip(4000);
                }
                // If autostart is ON
                else
                {
                    notifyIcon.BalloonTipIcon = ToolTipIcon.Info;
                    notifyIcon.BalloonTipTitle = "Meeting Start";
                    notifyIcon.BalloonTipText = "Meeting " + meeting.Title + " is about to start. Starting tracking time.";
                    notifyIcon.ShowBalloonTip(4000);

                    var wi = (from Model.Workitem workitem in WICollection
                              where workitem.WorkitemId == config.Configs["Meetings"].GetInt("WorkitemId")
                              select workitem).FirstOrDefault();

                    if (wi != null)
                    {
                        if (tracking.IsTracking)
                        {
                            previousTrackingId = tracking.WorkitemId;
                            tracking.StopTracking(false);
                        }

                        tracking.StartTracking(wi.WorkitemId, wi.Title);
                        tracking.MeetingId = meeting.MeetingId;
                        tracking.IsMeeting = true;
                    }
                    else
                    {
                        notifyIcon.BalloonTipIcon = ToolTipIcon.Error;
                        notifyIcon.BalloonTipTitle = "Meeting Start";
                        notifyIcon.BalloonTipText = "Could not autostart tracking item " + wi.WorkitemId + ", maybe it has been closed or assigned someone else.";
                        notifyIcon.ShowBalloonTip(4000);
                    }
                }
            }

            #endregion
        }

        private void gridWI_DataBindingComplete(object sender, DataGridViewBindingCompleteEventArgs e)
        {
            isDataBinding = true;
            DTOTime time;
            TimeSpan sumTime = new TimeSpan();
            foreach (DataGridViewRow row in gridWI.Rows)
            {
                time = colTimes.FirstOrDefault(x => x.WorkitemId == (int)row.Cells[0].Value);
                if (time == null)
                {
                    row.Cells["Time"].Value = "00:00";
                    row.Cells["TFSTime"].Value = "00.00";
                }
                else
                {
                    sumTime = new TimeSpan(colTimes.Where(x => x.WorkitemId == (int)row.Cells[0].Value).Sum(t => t.Time.Ticks));
                    row.Cells["Time"].Value = sumTime.ToString("hh\\:mm");
                    row.Cells["TFSTime"].Value = GetTfsTime(sumTime).ToString("N2");

                    globalTicks += sumTime.Ticks;
                }
            }

            tslblSumToday.Text = "Today: " + new TimeSpan(globalTicks).ToString("hh\\:mm") + " hrs.";

            globalTicks = 0;
            isDataBinding = false;
        }

        private void tsbtnStart_Click(object sender, EventArgs e)
        {
            if (tracking.IsTracking && tracking.Visible)
            {
                if (KryptonMessageBox.Show("Workitem " + tracking.WorkitemId + " is currently beeing tracked. Do you want to stop this and start workitem " + gridWI.SelectedRows[0].Cells[0].Value.ToString() + "?", "Start Tracking", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    tracking.StopTracking(true);
                }
                else
                {
                    return;
                }
            }
            this.Hide();
            tracking.StartTracking((int)gridWI.SelectedRows[0].Cells[0].Value, gridWI.SelectedRows[0].Cells[4].Value.ToString());
        }

        private void tsbtnStop_Click(object sender, EventArgs e)
        {
            tracking.StopTracking(true);
            previousTrackingId = 0;
        }

        private void tsBtnConfig_Click(object sender, EventArgs e)
        {
            frmConfiguration winConfig = new frmConfiguration(WICollection);
            winConfig.ShowDialog();
            config.Reload();
        }

        private void tsbtnDetails_Click(object sender, EventArgs e)
        {
            details.ShowDetail((int)gridWI.SelectedRows[0].Cells[0].Value, gridWI.SelectedRows[0].Cells[4].Value.ToString());
            if (details.ChangedData)
                LoadInfo();
        }

        private void tsbtnSave_Click(object sender, EventArgs e)
        {
            frmPostTimes winTimes = new frmPostTimes();
            winTimes.LoadTimes(this.WICollection);
            winTimes.ShowDialog();
        }

        private void tsbtnRefresh_Click(object sender, EventArgs e)
        {
            Enabled = false;
            Cursor = Cursors.WaitCursor;
            LoadInfo();
            GetAppointmentsInRange();
            Cursor = Cursors.Default;
            Enabled = true;
        }

        private void tscmbView_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (tscmbView.SelectedIndex == 0)
            {
                gridWI.Columns[2].Visible = false;
                gridWI.Columns[3].Visible = false;
                gridWI.Columns[5].Visible = false;
            }
            else
            {
                gridWI.Columns[2].Visible = true;
                gridWI.Columns[3].Visible = true;
                gridWI.Columns[5].Visible = true;
            }
        }

        private void notifyIcon_DoubleClick(object sender, EventArgs e)
        {
            this.Show();
            BringToFront();
            Activate();
        }

        private void frmMain_Resize(object sender, EventArgs e)
        {
            if (WindowState == FormWindowState.Minimized)
                Hide();
        }

        private void tracking_FormClosing(object sender, FormClosingEventArgs e)
        {
            e.Cancel = true;
            tracking.Hide();
            this.Show();

            TimeSpan sumTime = new TimeSpan();
            using (TfsModel modelo = new TfsModel())
            {
                var todayWis = modelo.Time
                    .Where(x => x.Start.Value.Date == DateTime.Today)
                    .Where(x => x.WorkitemId == tracking.WorkitemId).ToList();
                todayWis.ForEach(wi =>
                {
                    sumTime += wi.Finish.Value - wi.Start.Value;
                });
            }

            var row = (from DataGridViewRow r in gridWI.Rows
                       where (int)r.Cells[0].Value == tracking.WorkitemId
                       select r).First();

            // Full time
            row.Cells["Time"].Value = sumTime.ToString("hh\\:mm");

            // TFS time
            int minutes = sumTime.Minutes;
            double round = (double)minutes / 60 * 100;
            minutes = Convert.ToInt32(Math.Round(round / 25, MidpointRounding.ToEven) * 25);

            if (minutes == 100)
                row.Cells["TfsTime"].Value = (sumTime.Hours + 1).ToString().PadLeft(2, '0') + ".00";
            else
                row.Cells["TfsTime"].Value = sumTime.Hours.ToString().PadLeft(2, '0') + "." + minutes.ToString().PadLeft(2, '0');
        }

        private void frmMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (KryptonMessageBox.Show("Are you sure you want to close the application?", "Exit", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                if (tracking.IsTracking)
                    tracking.StopTracking(false);
            }
            else
            {
                e.Cancel = true;
            }
        }

        #endregion

        #region " Methods "

        private void LoadInfo()
        {
            using (TfsModel model = new TfsModel())
            {
                colTimes = (from Time time in model.Time
                            let fullTime = time.Finish.Value - time.Start.Value
                            where time.Start >= DateTime.Today && time.Start <= DateTime.Today.AddDays(1).AddSeconds(-1)
                            select new DTOTime
                            {
                                WorkitemId = time.WorkitemId,
                                Time = fullTime,
                                TFSTime = GetTfsTime(fullTime)
                            }).ToList();
            }

            WorkItemStore wis = Program.tfsSrv.GetService<WorkItemStore>();
            WorkItemCollection queryResults = wis.Query("Select [Id], [Title] From WorkItems WHERE [Assigned To] = @Me AND [State] <> 'Closed'");
            gridWI.DataSource = (from WorkItem wi in queryResults
                                 select new Model.Workitem
                                 {
                                     WorkitemId = wi.Id,
                                     Title = wi.Title,
                                     Subtype = (wi.Fields.Contains("Sub Type")) ? wi.Fields["Sub Type"].Value.ToString() : "",
                                     Scope = (wi.Fields.Contains("Scope Level")) ? wi.Fields["Scope Level"].Value.ToString() : "",
                                     IterationPath = wi.IterationPath,
                                     Estimated = (wi.Fields.Contains("Estimate")) ? Convert.ToDouble(wi.Fields["Estimate"].Value) : 0
                                 }).ToList();
        }

        private Outlook.Application GetOutlookInstance()
        {
            Outlook.Application application = null;

            // Check whether there is an Outlook process running.
            if (Process.GetProcessesByName("OUTLOOK").Length > 0)
            {
                // If so, use the GetActiveObject method to obtain the process and cast it to an Application object.
                application = Marshal.GetActiveObject("Outlook.Application") as Outlook.Application;
            }
            else
            {
                // If not, error. TODO: Start outlook
                KryptonMessageBox.Show("You must start Outlook to get appointments for today.", "Start Outlook", MessageBoxButtons.OK, MessageBoxIcon.Information);
                return null;
            }

            // Return the Outlook Application object.
            return application;
        }

        private void GetAppointmentsInRange()
        {
            colMeetings.Clear();

            var applic = GetOutlookInstance();

            if (applic == null)
                return;

            Outlook.Folder calFolder = applic.Session.GetDefaultFolder(Outlook.OlDefaultFolders.olFolderCalendar) as Outlook.Folder;
            DateTime start = DateTime.Today;
            DateTime end = start.AddDays(1);
            Outlook.Items rangeAppts = GetAppointmentsInRange(calFolder, start, end);
            if (rangeAppts != null)
            {
                foreach (Outlook.AppointmentItem appt in rangeAppts)
                {
                    colMeetings.Add(new Meeting()
                    {
                        MeetingId = colMeetings.Count,
                        Place = appt.Location,
                        Start = appt.Start,
                        Finish = appt.End,
                        Title = appt.Subject
                    });
                }
            }
        }

        /// <summary>
        /// Get recurring appointments in date range.
        /// </summary>
        /// <param name="folder"></param>
        /// <param name="startTime"></param>
        /// <param name="endTime"></param>
        /// <returns>Outlook.Items</returns>
        private Outlook.Items GetAppointmentsInRange(Outlook.Folder folder, DateTime startTime, DateTime endTime)
        {
            string filter = "[Start] >= '" + startTime.ToString("g")
                + "' AND [End] <= '" + endTime.ToString("g") + "'";

            try
            {
                Outlook.Items calItems = folder.Items;
                calItems.IncludeRecurrences = true;
                calItems.Sort("[Start]", Type.Missing);
                Outlook.Items restrictItems = calItems.Restrict(filter);
                if (restrictItems.Count > 0)
                {
                    return restrictItems;
                }
                else
                {
                    return null;
                }
            }
            catch { return null; }
        }

        private double GetTfsTime(TimeSpan time)
        {
            minutes = time.Minutes;
            round = (double)minutes / 60 * 100;
            minutes = Convert.ToInt32(Math.Round(round / 25, MidpointRounding.ToEven) * 25);

            if (minutes == 100)
                return (time.Hours + 1);
            else
                return (time.Hours + (minutes / 100D));
        }

        #endregion

        private void tslblInfo_Click(object sender, EventArgs e)
        {
            frmAbout about = new frmAbout();
            about.ShowDialog();
        }
    }
}
